from pieceConstants import *

class Square:
    '''
    piece and board square class
    '''
    def __init__(self, x, y, owner):
        '''
        basic constructor
        '''
        self.owner = owner
        self.x = x
        self.y = y
            
    def getNeighbors(self, squares):
        '''
        finds the neighbor squares in all 8 directions
        
        Returns: neighbors of this square
        '''
        neighbors = []
        for s in squares:
            if s.x == self.x + 1 and s.y == self.y:
                neighbors.append(s)
            if s.x == self.x - 1 and s.y == self.y:
                neighbors.append(s)
            if s.x == self.x and s.y == self.y + 1:
                neighbors.append(s)
            if s.x == self.x and s.y == self.y - 1:
                neighbors.append(s)
            if s.x == self.x + 1 and s.y == self.y + 1:
                neighbors.append(s)
            if s.x == self.x + 1 and s.y == self.y - 1:
                neighbors.append(s)
            if s.x == self.x - 1 and s.y == self.y + 1:
                neighbors.append(s)
            if s.x == self.x - 1 and s.y == self.y - 1:
                neighbors.append(s)
        return neighbors
    
class Board:
    '''
    board class
    '''
    def __init__(self, size = 10):
        '''
        basic constructor
        '''
        self.pieces = []
        self.undoPieces = []
        self.squares = []
        #artificial limit due to laziness with the board labeling
        if size > 26: 
            self.size = 10
        else:
            self.size = size
        for j in range(0, self.size):
            for i in range(0, self.size):
                self.squares.append(Square(i, j, NT))
        
    def isValidMove(self, piece, x, y, squares):
        '''
        checks if a piece placed at x, y is valid
        
        Returns: boolean valid if piece is placeable
        '''
        valid = False
        if piece != None:
            piece.setLocation(x, y, piece.orientation)
            valid = True
            for s in piece.squares:
                try:
                    if squares[s.x * self.size + s.y].owner in [NT, TER(s.owner)] and s.x <= self.size - 1 and s.y <= self.size - 1 and s.x >= 0 and s.y >= 0:
                        valid = valid and True
                    else:
                        valid = False
                except:
                    valid = False
        return valid        
        
    def placePiece(self, piece, x, y, dontCheck = False, squares = [], doSquares = False, realOwner = ''):
        '''
        places piece on the board
        
        Returns: list of squares with the piece added
        '''
        if doSquares:
            newSquares = squares
        else:
            newSquares = []
            for s in self.squares:
                newSquares.append(Square(s.x, s.y, s.owner))
        if self.isValidMove(piece, x, y, newSquares) or dontCheck:            
            for s in piece.squares:
                if newSquares[s.x * self.size + s.y].owner not in [NT, TER(s.owner), TER(realOwner)]:
                    newSquares[s.x * self.size + s.y].owner = O
                else:
                    newSquares[s.x * self.size + s.y].owner = piece.owner
        return newSquares
    
    def removePiece(self, piece):
        '''
        removes a piece from the board
        
        Returns: removed piece
        '''
        pieceToRemove = None
        for p in self.undoPieces:
            same = True
            for attr in vars(p).keys():
                if getattr(p, attr) == getattr(piece, attr):
                    same = same and True
            if same:
                pieceToRemove = p
    
        if pieceToRemove != None:
            for p in pieceToRemove.squares:
                for s in self.squares:
                    if s.x == p.y and s.y == p.x:
                        s.owner = NT
            self.pieces.remove(pieceToRemove)
            self.undoPieces.remove(pieceToRemove)
        
        return pieceToRemove            
    
    def updateBoard(self, turn):
        '''
        sets captured territory
        
        Returns: captured piece
        '''
        if turn == P1:
            prevTurn = P2
        else:
            prevTurn = P1
        returnPieces = self.markTerritory(turn)
        for p in returnPieces:
            for r in p.squares:
                for s in self.squares:
                    if s.x == r.y and s.y == r.x:
                        s.owner = prevTurn + T
            self.pieces.remove(p)
            self.undoPieces = []
        self.markTerritory(None)
        return returnPieces
        
    def markTerritory(self, turn):
        '''
        marks territory player has captured
        
        Returns: captured piece
        '''
        index = 0
        newSquares = []
        removedPieces = []
        
        for s in self.squares:
            if s.owner not in [P1T, P2T]:
                newSquares.append(Square(s.x, s.y, s.owner))
            else:
                newSquares.append(Square(s.x, s.y, NT))
                
        for p in self.pieces:
            if p.owner in [turn, C] and turn != None and len(self.pieces) > 2:
                removedPieces.append(p)
                for x in p.squares:
                    for s in newSquares:
                        if s.x == x.y and s.y == x.x:
                            s.owner = NT
        
        for s in newSquares:
            if self.flood(newSquares, s.x, s.y, [NT], A + str(index)):
                index = index + 1
        teritory = []
        for i in range(0,index):
            teritory.append(NT)
        for i in range(0,index):
            containedPieces = []
            neighbors = {}
            areaSquares = []
            for s in newSquares:
                if s.owner == A + str(i):
                    areaSquares.append(s)
                    for n in s.getNeighbors(newSquares):
                        neighbors[n.owner] = 1
            for p in self.pieces:
                if p.owner in [turn, C]:
                    isContained = False
                    for s in areaSquares:
                        for x in p.squares:
                            if s.x == x.y and s.y == x.x:
                                isContained = isContained or True
                    if isContained: 
                        containedPieces.append(p)
            
            x = neighbors.keys()
            try:
                x.remove(A + str(i))
            except:
                pass
            if len(x) == 1 and x[0] == P1 and len(containedPieces) < 2:
                teritory[i] = P1T
            elif len(x) == 1 and x[0] == P2 and len(containedPieces) < 2:
                teritory[i] = P2T
        for i in range(0,index):
            for s in newSquares:
                if s.owner == A + str(i):
                    s.owner = teritory[i]
        returnPieces = []
        for p in removedPieces:
            doRemove = False
            for s in p.squares:
                if newSquares[s.x * self.size + s.y].owner not in [s.owner, NT, TER(s.owner)]:
                    doRemove = doRemove or True
                    newSquares[s.x * self.size + s.y].owner = O
                else:
                    newSquares[s.x * self.size + s.y].owner = p.owner
            if doRemove:
                returnPieces.append(p)
        
        self.squares = newSquares
        
        return returnPieces
        
    def flood(self, squares, x, y, target, replace):
        '''
        fills areas of the board that are surrounded
        
        Returns: True if done filling
        '''
        for s in squares:
            if s.x == x and s.y == y:
                if s.owner not in target:
                    return False
                else:
                    s.owner = replace
                    self.flood(squares, s.x, s.y + 1, target, replace)
                    self.flood(squares, s.x + 1, s.y, target, replace)
                    self.flood(squares, s.x, s.y - 1, target, replace)
                    self.flood(squares, s.x - 1, s.y, target, replace)
                    self.flood(squares, s.x + 1, s.y + 1, target, replace)
                    self.flood(squares, s.x + 1, s.y - 1, target, replace)
                    self.flood(squares, s.x - 1, s.y + 1, target, replace)
                    self.flood(squares, s.x - 1, s.y - 1, target, replace)
                    return True
                
class Piece:
    '''
    piece class
    '''
    def __init__(self, owner, name, geometry):
        '''
        basic constructor
        '''
        self.name = name
        self.owner = owner
        self.squares = []
        self.orientation = N
        self.geometry = geometry
        self.x = 0
        self.y = 0
        self.setLocation(self.x, self.y, self.orientation)
               
    def setLocation(self, x, y, newOrientation):
        '''
        sets location and rotation of piece
        '''
        self.orientation = newOrientation
        self.x = x
        self.y = y
        self.squares = []
        newGeometry = REORIENT(self.orientation,self.geometry)
        for (x, y) in newGeometry:
            self.squares.append(Square(self.x + x, self.y + y, self.owner))
    
    def getNeighbors(self, pieces):
        '''
        finds piece neighbors in 4 directions
        
        Returns: neighbors of this piece
        '''
        neighbors = []
        for p in pieces:
            if p != self:
                isANeighbor = False
                for s in p.squares:
                    for i in self.squares:
                        if s.x == i.x + 1 and s.y == i.y:
                            isANeighbor = isANeighbor or True
                        if s.x == i.x - 1 and s.y == i.y:
                            isANeighbor = isANeighbor or True
                        if s.x == i.x and s.y + 1 == i.y:
                            isANeighbor = isANeighbor or True
                        if s.x == i.x and s.y - 1 == i.y:
                            isANeighbor = isANeighbor or True
                if isANeighbor:
                    neighbors.append(p)
        return neighbors